AGRICULTURE

Overview

The AGRICULTURE function fits specialized mathematical models for agricultural yield analysis to experimental data using non-linear least squares regression. These models are commonly used in crop science and agronomy to describe relationships between plant density, fertilizer application, and crop yield.

This function implements eight yield-density and fertilizer response models:

  • Bleasdale-Nelder Yield Density: A four-parameter model (a, b, c, f) for yield-density relationships: y = (a + bx^f)^{-1/c}
  • Bleasdale-Nelder Simplified: A three-parameter simplification: y = (a + bx)^{-1/c}
  • Farazdaghi-Harris Competition: Models inter-plant competition: y = (a + bx^c)^{-1}
  • Holliday Yield Density Quadratic: Includes quadratic density effects: y = (a + bx + cx^2)^{-1}
  • Holliday Modified Rational: A rational function form: y = a / (1 + bx + cx^2)
  • Nelder Fertilizer Response: Models fertilizer response curves with an asymptotic maximum
  • Yield-Fertilizer Exponential Decay: Models diminishing returns: y = a + br^x where 0 < r < 1
  • Yield-Fertilizer Natural Exponential: Uses natural exponential decay: y = a + be^{-kx}

The function uses scipy.optimize.curve_fit to perform the optimization via the Levenberg-Marquardt algorithm. For each model, intelligent initial parameter guesses are computed from the input data to improve convergence. The function returns the fitted parameter values along with their standard errors, which are derived from the covariance matrix of the fit.

These models originate from foundational agricultural research, including work by Bleasdale and Nelder (1960) on plant competition, Holliday (1960) on yield-density relationships, and Farazdaghi and Harris (1968) on competitive interactions. For more information on non-linear curve fitting methodology, see the SciPy optimization documentation.

This example function is provided as-is without any representation of accuracy.

Excel Usage

=AGRICULTURE(xdata, ydata, agriculture_model)
  • xdata (list[list], required): The xdata value
  • ydata (list[list], required): The ydata value
  • agriculture_model (str, required): The agriculture_model value

Returns (list[list]): 2D list [param_names, fitted_values, std_errors], or error string.

Examples

Example 1: Demo case 1

Inputs:

agriculture_model xdata ydata
bleasdale_nelder_yield_density 0.1 0.5672982383388386
1.3250000000000002 0.33957893215370566
2.5500000000000003 0.07329429756158326
3.7750000000000004 0.012377205194408224
5 0.013794171321237347

Excel formula:

=AGRICULTURE("bleasdale_nelder_yield_density", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {0.5672982383388386;0.33957893215370566;0.07329429756158326;0.012377205194408224;0.013794171321237347})

Expected output:

"non-error"

Example 2: Demo case 2

Inputs:

agriculture_model xdata ydata
bleasdale_nelder_simplified 0.1 0.5516374948796566
1.3250000000000002 0.4493055231416069
2.5500000000000003 0.3839515385505842
3.7750000000000004 0.3333955941114058
5 0.3065391490538524

Excel formula:

=AGRICULTURE("bleasdale_nelder_simplified", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {0.5516374948796566;0.4493055231416069;0.3839515385505842;0.3333955941114058;0.3065391490538524})

Expected output:

"non-error"

Example 3: Demo case 3

Inputs:

agriculture_model xdata ydata
farazdaghi_harris_competition 0.1 0.3645280967136649
1.3250000000000002 0.23084459235849314
2.5500000000000003 0.12735361747704974
3.7750000000000004 0.06958788363231085
5 0.05160629269753579

Excel formula:

=AGRICULTURE("farazdaghi_harris_competition", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {0.3645280967136649;0.23084459235849314;0.12735361747704974;0.06958788363231085;0.05160629269753579})

Expected output:

"non-error"

Example 4: Demo case 4

Inputs:

agriculture_model xdata ydata
holliday_yield_density_quadratic 0.1 0.35168646655190283
1.3250000000000002 0.14611622513801112
2.5500000000000003 0.0644988821745923
3.7750000000000004 0.02673172343382443
5 0.021830092073556524

Excel formula:

=AGRICULTURE("holliday_yield_density_quadratic", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {0.35168646655190283;0.14611622513801112;0.0644988821745923;0.02673172343382443;0.021830092073556524})

Expected output:

"non-error"

Example 5: Demo case 5

Inputs:

agriculture_model xdata ydata
holliday_modified_rational 0.1 2.476514135825012
1.3250000000000002 0.5593798396545494
2.5500000000000003 0.22005645510585548
3.7750000000000004 0.055582501026831814
5 0.07375803414954989

Excel formula:

=AGRICULTURE("holliday_modified_rational", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {2.476514135825012;0.5593798396545494;0.22005645510585548;0.055582501026831814;0.07375803414954989})

Expected output:

"non-error"

Example 6: Demo case 6

Inputs:

agriculture_model xdata ydata
nelder_fertilizer_response 0.1 0.22776420617719842
1.3250000000000002 0.1787121171937119
2.5500000000000003 0.14815038131711614
3.7750000000000004 0.12726844779262309
5 0.10676484833455836

Excel formula:

=AGRICULTURE("nelder_fertilizer_response", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {0.22776420617719842;0.1787121171937119;0.14815038131711614;0.12726844779262309;0.10676484833455836})

Expected output:

"non-error"

Example 7: Demo case 7

Inputs:

agriculture_model xdata ydata
yield_fertilizer_exponential_decay 0.1 3.7475442808523565
1.3250000000000002 3.223161367719532
2.5500000000000003 2.9896743124843193
3.7750000000000004 2.8859095042201393
5 2.7988484749968507

Excel formula:

=AGRICULTURE("yield_fertilizer_exponential_decay", {0.1;1.3250000000000002;2.5500000000000003;3.7750000000000004;5}, {3.7475442808523565;3.223161367719532;2.9896743124843193;2.8859095042201393;2.7988484749968507})

Expected output:

"non-error"

Example 8: Demo case 8

Inputs:

agriculture_model xdata ydata
yield_fertilizer_natural_exponential 0.01 3.7582088335464605
2.0075 2.747280997232976
4.005 2.762931508104057
6.0024999999999995 2.7804082427601458
8 2.7453249815611165

Excel formula:

=AGRICULTURE("yield_fertilizer_natural_exponential", {0.01;2.0075;4.005;6.0024999999999995;8}, {3.7582088335464605;2.747280997232976;2.762931508104057;2.7804082427601458;2.7453249815611165})

Expected output:

"non-error"

Python Code

import numpy as np
from scipy.optimize import curve_fit as scipy_curve_fit
import math

def agriculture(xdata, ydata, agriculture_model):
    """
    Fits agriculture models to data using scipy.optimize.curve_fit. See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html for details.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        xdata (list[list]): The xdata value
        ydata (list[list]): The ydata value
        agriculture_model (str): The agriculture_model value Valid options: Bleasdale Nelder Yield Density, Bleasdale Nelder Simplified, Farazdaghi Harris Competition, Holliday Yield Density Quadratic, Holliday Modified Rational, Nelder Fertilizer Response, Yield Fertilizer Exponential Decay, Yield Fertilizer Natural Exponential.

    Returns:
        list[list]: 2D list [param_names, fitted_values, std_errors], or error string.
    """
    def _validate_data(xdata, ydata):
        """Validate and convert both xdata and ydata to numpy arrays."""
        for name, arg in [("xdata", xdata), ("ydata", ydata)]:
            if not isinstance(arg, list) or len(arg) < 2:
                raise ValueError(f"{name}: must be a 2D list with at least two rows")
            vals = []
            for i, row in enumerate(arg):
                if not isinstance(row, list) or len(row) == 0:
                    raise ValueError(f"{name} row {i}: must be a non-empty list")
                try:
                    vals.append(float(row[0]))
                except Exception:
                    raise ValueError(f"{name} row {i}: non-numeric value")
            if name == "xdata":
                x_arr = np.asarray(vals, dtype=np.float64)
            else:
                y_arr = np.asarray(vals, dtype=np.float64)

        if x_arr.shape[0] != y_arr.shape[0]:
            raise ValueError("xdata and ydata must have the same number of rows")
        return x_arr, y_arr

    # Model definitions dictionary
    models = {
        'bleasdale_nelder_yield_density': {
            'params': ['a', 'b', 'c', 'f'],
            'model': lambda x, a, b, c, f: np.power(a + b * np.power(x, f), -1.0 / c),
            'guess': lambda xa, ya: (1.0, 1.0, 1.0, 1.0),
        },
        'bleasdale_nelder_simplified': {
            'params': ['a', 'b', 'c'],
            'model': lambda x, a, b, c: np.power(a + b * x, -1.0 / c),
            'guess': lambda xa, ya: (1.0, 1.0, 1.0),
        },
        'farazdaghi_harris_competition': {
            'params': ['a', 'b', 'c'],
            'model': lambda x, a, b, c: np.power(a + b * np.power(x, c), -1.0),
            'guess': lambda xa, ya: (1.0, 1.0, 1.0),
        },
        'holliday_yield_density_quadratic': {
            'params': ['a', 'b', 'c'],
            'model': lambda x, a, b, c: np.power(a + b * x + c * np.square(x), -1.0),
            'guess': lambda xa, ya: (1.0, 1.0, 0.1),
        },
        'holliday_modified_rational': {
            'params': ['a', 'b', 'c'],
            'model': lambda x, a, b, c: a / (1.0 + b * x + c * np.square(x)),
            'guess': lambda xa, ya: (float(np.max(ya) if np.max(ya) else 1.0), 0.1, 0.01),
        },
        'nelder_fertilizer_response': {
            'params': ['a', 'b0', 'b1', 'b2'],
            'model': lambda x, a, b0, b1, b2: (x + a) / (b0 + b1 * (x + a) + b2 * np.square(x + a)),
            'guess': lambda xa, ya: (1.0, 1.0, 0.1, 0.01),
            'maxfev': 100000,
        },
        'yield_fertilizer_exponential_decay': {
            'params': ['a', 'b', 'r'],
            'model': lambda x, a, b, r: a + b * np.power(r, x),
            'guess': lambda xa, ya: (float(np.min(ya)), float(np.ptp(ya) if np.ptp(ya) else 1.0), 0.5),
            'bounds': ([-np.inf, -np.inf, 0.0], [np.inf, np.inf, 1.0]),
        },
        'yield_fertilizer_natural_exponential': {
            'params': ['a', 'b', 'k'],
            'model': lambda x, a, b, k: a + b * np.exp(-k * x),
            'guess': lambda xa, ya: (float(np.min(ya)), float(np.ptp(ya) if np.ptp(ya) else 1.0), 0.5),
            'bounds': ([-np.inf, -np.inf, 0.0], np.inf),
        }
    }

    # Validate model parameter
    if agriculture_model not in models:
        return f"Invalid model: {str(agriculture_model)}. Valid models are: {', '.join(models.keys())}"

    model_info = models[agriculture_model]

    # Validate and convert input data
    try:
        x_arr, y_arr = _validate_data(xdata, ydata)
    except ValueError as e:
        return f"Invalid input: {e}"

    # Perform curve fitting
    try:
        p0 = model_info['guess'](x_arr, y_arr)
        bounds = model_info.get('bounds', (-np.inf, np.inf))
        maxfev = model_info.get('maxfev', 10000)
        if bounds == (-np.inf, np.inf):
            popt, pcov = scipy_curve_fit(model_info['model'], x_arr, y_arr, p0=p0, maxfev=maxfev)
        else:
            popt, pcov = scipy_curve_fit(model_info['model'], x_arr, y_arr, p0=p0, bounds=bounds, maxfev=maxfev)

        fitted_vals = [float(v) for v in popt]
        for v in fitted_vals:
            if math.isnan(v) or math.isinf(v):
                return "Fitting produced invalid numeric values (NaN or inf)."
    except ValueError as e:
        return f"Initial guess error: {e}"
    except Exception as e:
        return f"curve_fit error: {e}"

    # Calculate standard errors
    std_errors = None
    try:
        if pcov is not None and np.isfinite(pcov).all():
            std_errors = [float(v) for v in np.sqrt(np.diag(pcov))]
    except Exception:
        pass

    return [model_info['params'], fitted_vals, std_errors] if std_errors else [model_info['params'], fitted_vals]

Online Calculator